<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "https://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="zh-CN">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="Content-Language" content="zh-CN" />
<!--[if lt IE 9]><style type="text/css">@font-face { font-family: "JinBuGuoWebMono"; src: url("/d/mono.eot"); }</style><![endif]-->
<style media="all" type="text/css">
@font-face { font-family: "JinBuGuoWebMono"; src: url("http://www.jinbuguo.com/d/mono.ttf") format("truetype"); }
* { font-family: "JinBuGuoWebMono", "Ubuntu Mono", "Consolas", "Menlo", monospace; }
body { margin:10px; }
h1,h2 { text-align:center; background:#ddd; }
h2 { margin: 10px 5%; }
h2#auth_name {  background:#fff; }
dt { margin-top: 0.5em; color:#600; }
pre { background: #edc; }
</style>
<title>Linux 环境 SSD(固态硬盘)使用指南 [金步国]</title>
<script> var _hmt = _hmt || []; (function() { var hm = document.createElement("script"); hm.src = "https://hm.baidu.com/hm.js?d286c55b63a3c54a1e43d10d4c203e75"; var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(hm, s); })(); </script>
</head>

<body>
<h1>Linux 环境 SSD(固态硬盘)使用指南</h1><h2 id="auth_name">作者：<strong><a href="../index.html">金步国</a></strong></h2>
<hr />
<h3>版权声明</h3>
<p>本文作者是一位开源理念的坚定支持者，所以本文虽然不是软件，但是遵照开源的精神发布。</p>
<ul>
<li>无担保：本文作者不保证作品内容准确无误，亦不承担任何由于使用此文档所导致的损失。</li>
<li>自由使用：任何人都可以自由的<u>阅读/链接/打印</u>此文档，无需任何附加条件。</li>
<li>名誉权：任何人都可以自由的<u>转载/引用/再创作</u>此文档，但必须保留作者署名并注明出处。</li>
</ul>
<h3>其他作品</h3>
<p>本文作者十分愿意与他人分享劳动成果，如果你对我的其他翻译作品或者技术文章有兴趣，可以在如下位置查看现有的作品集：</p>
<ul>
<li><a href="../index.html">金步国作品集</a> [ <a href="../index.html">http://www.jinbuguo.com/</a> ]</li>
</ul>
<h3>联系方式</h3>
<p>由于作者水平有限，因此不能保证作品内容准确无误。如果你发现了作品中的错误(哪怕是错别字也好)，请来信指出，任何提高作品质量的建议我都将虚心接纳。</p>
<ul>
<li>Email(QQ)：70171448在QQ邮箱</li>
</ul>
<hr />


<h2>前言</h2>
<p>因为SSD有着诸多与传统机械式硬盘不同的特点，而且这些特点导致了SSD在实际使用中的性能和寿命与其使用方法紧密相关。所以并不是说买来一块非常牛X的SSD就万事大吉了。另一方面，SSD的性能并不像机械式硬盘那样比较稳定(浮动范围小)，而是呈现出一种离散的特点(浮动范围大)，最佳状态与最差状态相差甚远(上百倍)，所以如何避免最差状态比如何发挥最佳状态更重要。</p>

<h2>分区对齐</h2>
<p>如果要对一块SSD进行分区，然后再使用，就会遇到这个问题。当然，也可以不对它进行分区以避免这个问题。对于Linux来说，不分区直接使用就是直接格式化 /dev/sdx 设备(比如：mkfs.xfs /dev/sdx)，然后再 mount /dev/sdx /mnt/point </p>
<p>考虑到使用习惯、预留空间(见下文)的问题，以及安装引导程序(如：grub)的要求，不做分区直接使用并不是个好主意。幸运的是，分区对齐问题是可以得到完美解决的。</p>
<h3>害处</h3>
<p>如果SSD上的分区分割点处于未对齐SSD block size倍数的状态，SSD效能就不能完全发挥，系统可能会有卡顿的感觉，另外，未对齐的分割点还会造成SSD在连续写入小文件时，增加无谓的SSD额外写入/擦除动作从而大幅影响SSD的寿命，而这偏偏是SSD使用中最该避免的。</p>
<p>分区不对齐，对4KB随机写入的影响最大，实际测试表明，在其他因素相同的情况下，分区不对其导致的性能差距最大可达<strong>10倍</strong>。</p>

<h3>CHS、LBA、block(块)</h3>
<p>在了解事情的来龙去脉之前，先得把传统硬盘的几个概念拿出来复习一下。</p>
<p>CHS(Cylinder-head-sector)是最早用于访问硬盘的寻址方式。虽然CHS的值现在早已不再与实际的物理值相对应(只是个逻辑值)，但是依然有许多磁盘管理程序(比如fdisk/cfdisk)使用CHS的方式来理解硬盘。</p>
<p><img src="SSD_files/CHS.jpg" /></p>
<h4>side/head</h4>
<p>硬盘一般是由一片或几片圆形薄膜叠加而成。每个圆形薄膜都有两个"面"(Side)，这两个面都是用来存储数据的。盘面由上而下从"0"开始编号，依次称为0面、1面、2面……由于每个面都专有一个读写磁头，也常用0头(head)、1头……称之。硬盘面数(或头数)，少的只有2面，多的可达数十面。</p>
<p>按照CHS规范，head使用8bit编址，因此最多可以有256个磁头(0-255)。但是由于某些古董程序只能最大支持到255个磁头，出于兼容性考虑，绝大多数场合的默认值依然是255个磁头。</p>
<h4>track</h4>
<p>读写硬盘时，磁头不动，磁盘是旋转的，则连续写入的数据是排列在一个圆周上的。我们称这样的圆周为一个磁道(Track)。</p>
<p>磁头不动，就是在一个磁道上读写；磁头移动，就会在不同磁道上读写。</p>
<p>按照CHS寻址规范，由外向内从"0"开始顺序编号。不过其编址时使用的位宽并不是一个定值(取决于不同的规范)，我们姑且可以认为其足够大。</p>
<h4>cylinder</h4>
<p>各面上磁道号相同的磁道合起来，称为一个柱面(cylinder)。也就是距轴的距离相同的一组track所形成的圆筒。</p>
<p>按照CHS寻址规范，cylinder和track一样，也由外向内从"0"开始顺序编号。并且其编址位宽足够大。</p>
<p class="high_light">cylinder也是磁盘分区时的最小单位，分区是按磁道和柱面连续分布的(即每个分区，都是粗细连续的一组筒子)。</p>
<p>下面是一个fdisk的输出，主意最后一行：</p>
<pre>
# fdisk /dev/sda
Command (m for help): n
Command action
   e   extended
   p   primary partition (1-4)
p
Partition number (1-4): 1
First cylinder (1-524, default 1):
</pre>
<h4>Sector</h4>
<p>一个track上可以容纳"较多"的数据，而主机读写时往往并不需要一次读写那么多，于是，磁道又被按512或4096字节划分成若干段，每段称为一个扇区(Sector)。一个扇区的大小是固定的512或4096字节。</p>
<p>计算机对硬盘的读写，是以扇区为最小单位。即使只读某一个字节，也必须一次把这个字节所在的扇区中的512或4096字节全部读入内存。</p>
<p>按照CHS寻址规范，Sector的编号始终是从"<strong>1</strong>"开始的(不是"0")。由于使用6bit编址，所以其最大值是63，也就是说既不存在 sector 0 也不存在 sector 64 。目前所有机械式硬盘在逻辑CHS模式中都使用63这个最大值。</p>
<h4>block</h4>
<p>Block是文件系统的最小存取空间。一个 Block 最多仅能容纳一个文件（即不存在多个文件同一个block的情况）。如果一个文件比block小，他也会占用一个block，因而block中空余的空间会浪费掉。而一个大文件，可以占多个甚至数十个成百上千万的block。</p>
<pre>
# df
/                  (/dev/dsk/c0t3d0s0 ):  573548 blocks   226057 files
/proc              (/proc             ):       0 blocks     3854 files
/var               (/dev/dsk/c0t3d0s1 ): 1897206 blocks   250028 files
/var/run           (swap              ):  611424 blocks    26300 files
/tmp               (swap              ):  611424 blocks    26300 files
</pre>
<p>sector 和block的差别</p>
<ul>
<li>sector 是硬盘存取的最小单位，是512B或4096B</li>
<li>block 是文件系统的最小存取单位，可以随意设定，但必须是sector的整数倍。如ext2 fs的block缺省是4k</li>
</ul>
<p>应该根据自己系统应用的特点，合理规划block size：若block太大，则存取小文件时，有空间浪费的问题；若block太小，则硬盘的 Block 数目会大增，而造成 inode 在指向 block 时候的一些搜寻时间的增加，又会造成大文件读写方面的效率较差。</p>
<h4 id="磁盘容量">磁盘容量</h4>
<p>先看一个500G的硬盘在 fdisk -l 时显示的信息：</p>
<pre>
# fdisk -l /dev/sda

Disk /dev/sda: 500.1 GB, 500107862016 bytes
255 heads, 63 sectors/track, 60801 cylinders, total 976773168 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00000000

   Device Boot      Start         End      Blocks   Id  System
/dev/sda1              63     8000369     4000153+  83  Linux
/dev/sda2         8000370    95891984    43945807+  83  Linux
/dev/sda3        95891985   427923404   166015710   83  Linux
/dev/sda4       427923405   976768064   274422330   83  Linux
</pre>
<p>可以看出，磁盘总容量=磁头数*每磁道扇区数*柱面数*扇区大小。当然，这里的磁头数、扇区数、柱面数并不是物理真实值，而是逻辑值。甚至对于4KB扇区的硬盘，扇区大小也可能是逻辑值。</p>
<h4>LBA寻址机制</h4>
<p>上面说的CHS寻址方式其实在实践中早就被丢进垃圾箱了。目前在实践中真正使用的是48位LBA(Logical Block Address)寻址方式。LBA是非常单纯的一种寻址模式：从0开始编号来定位区块(扇区)，第一区块LBA=0，第二区块LBA=1，依此类推。以每扇区512字节计算，容量上限可达128PB。LBA寻址模式完全屏蔽了硬盘的物理结构，而将其简单的抽象成一维条带，非常便于操作系统的理解。</p>

<h3>原因分析</h3>
<p>了解了上面的知识之后，现在知道了机械硬盘从很早前开始，扇区大小就被定义为512字节，而最新的"先进格式"机械盘终于把物理扇区提高到4KB(4096字节)。机械式硬盘的最小操作单位是扇区，也就是说，无论读取还是写入1个字节、10个字节、500字节，实际的操作都是512字节。当然，对于4KB扇区的机械式硬盘来说，所有读写操作都会被向上取整到4KB的整数倍。</p>
<p>但SSD的操作方式与此不同。SSD不像HDD那样只有读/写两种操作而且还是统一的，SSD有三种操作：读/写/擦除。闪存的读写单位是4KB或8KB大小的<strong>页</strong>，而且闪存的擦除(又叫编程)操作是按照128或256页大小的<strong>块</strong>来操作的。</p>
<p>传统上，LBA模式的HDD第一个分区的起始点从63逻辑扇区(63x512B=31.5KB)开始，对于扇区大小为512B的HDD来说，这当然没什么问题。但对于SSD和新式HDD来说，就会造成用户的第一个数据的前4KB会存放在系统"逻辑扇区"的31.5KB~35.5KB间，这样持续下去会造成后面所有的数据都会卡在2个物理扇区(页)容量之间，我们知道扇区(页)为磁盘写入的最小单位，如果卡在2个扇区(页)之间，写入的时候就需要进行读-改写操作(对SSD来说读-擦-写)，造成性能的下降。</p>
<p><img src="SSD_files/align.jpg" /></p>
<h3>分割点应该放在哪里呢？</h3>
<p>简单的说，应该放在SSD最大操作单位"块"，也就是block size整数倍的位置。</p>
<p>对于单通道的闪存设备，这很简单，也很好理解。但事实上，几乎所有的SSD都不是单通道的，这时候应该空出多少呢？</p>
<p>多通道的闪存设备也是把数据拆成一个一个的块，然后分别对每个通道进行读写操作，这个块的大小和所使用的闪存芯片的块的大小是一样的。如果多通道SSD会把数据再进一步拆分然后才写到各个通道的话，情况就会变得复杂了。不过目前的SSD主控似乎还没有这么聪明，目前多通道写入时最小的数据单位依然是闪存块的大小。因此，在目前的情况下，我们完全可以不考虑设备是几通道的，直接把分区分割点设在block size整数倍的位置即可。也许未来需要把分割点设置在"通道数*block-size"整数倍的位置？</p>

<h3>FDISK</h3>
<p>在给出最终解决方案之前，先来了解一下fdisk工具：</p>
<pre>
# fdisk -h
Usage:
 fdisk [options] &lt;disk&gt;    change partition table
 fdisk [options] -l &lt;disk&gt; list partition table(s)
 fdisk -s &lt;partition&gt;      give partition size(s) in blocks

Options:
 -b &lt;size&gt;             sector size (512, 1024, 2048 or 4096)
 -c[=&lt;mode&gt;]           compatible mode: 'dos' or 'nondos' (default)
 -h                    print this help text
 -u[=&lt;unit&gt;]           display units: 'cylinders' or 'sectors' (default)
 -v                    print program version
 -C &lt;number&gt;           specify the number of cylinders
 -H &lt;number&gt;           specify the number of heads
 -S &lt;number&gt;           specify the number of sectors per track
</pre>
<p>我们通常并不使用任何选项，但是为了强制分割点的位置对齐，就必须强制指定如下三个参数：</p>
<dl>
<dt>-b</dt><dd>指定扇区的大小，只能取 512/1024/2048/4096 之一。<br /><span class="high_light">注意，经过本人实测，使用大于512的其他值(假定是N)，会导致fdisk只能使用到真实容量的512/N。所以最好不要使用该选项。</span></dd>
<dt>-H</dt><dd>指定磁头数，只能取 1-256 之间的整数。</dd>
<dt>-S</dt><dd>指定每磁道扇区数，只能取 1-63 之间的整数。</dd>
</dl>
<p>这三个参数值的乘积就是一个逻辑柱面的总大小，也就是最小分割单位了。</p>

<h3>解决方案</h3>
<p>经过前面的讲解，现在知道，只要把"块"作为SSD的最小单位来划分磁盘就能完美解决分区对齐的问题。</p>
<p>现在假定我们拿到一块SSD的参数是这样的：每个页的大小是8KB、每256个页组成一个块，那么也就是说我们必须以256*8KB=2097152B=2048KB=2MB为最小单位对其进行分区。也就是说，我们可以通过强制指定fdisk的命令行参数，使每个柱面(cylinder)的大小为2MB即可达到目的(假定每扇区512字节)：</p>
<pre>fdisk -u=cylinders  -H 128 -S 32  /dev/sdx</pre>
<p>如果觉得每个柱面(cylinder)的大小为2MB还是不够大，那么可以加大到4MB(假定每扇区512字节)：</p>
<pre>fdisk -u=cylinders  -H 256 -S 32  /dev/sdx</pre>
<p class="high_light">此外，还需要注意一个细节，那就是第一个分区的起点不能从默认的第一个柱面开始，而是要从第2个柱面开始，否则第一个分区有可能依然不会对齐。如下图所示：</p>
<pre>
Command (m for help): n
Command action
   e   extended
   p   primary partition (1-4)
p
Partition number (1-4, default 1):
Using default value 1
First cylinder (1-800, default 1): <span class="high_light">2</span>
Last cylinder, +cylinders or +size{K,M,G} (2-800, default 800): 500
</pre>
<p>注意 First cylinder 那一行，那里的默认值是1，但是不能用它，要手动修改为2。</p>
<p>导致这个问题的原因，据Google说是fdisk会对从 cylinder 1 开始的扇区特殊对待，自作主张的向前平移分区起点。不过，既然新版本的fdisk已经修正了这个bug，我们就不必再去考古了。</p>
<h3>对齐检查</h3>
<p>如何检查分区是否确实已经对齐呢？方法是使用"fdisk -u=sectors -l /dev/sdx"查看。例如：</p>
<pre>
# fdisk -u=sectors -l /dev/sda

Disk /dev/sda: 671 MB, 671088640 bytes
128 heads, 32 sectors/track, 320 cylinders, total 1310720 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x7bf5a16d

   Device Boot      Start         End      Blocks   Id  System
/dev/sda1              63       40959       20448+  83  Linux
/dev/sda2           40960       79871       19456   83  Linux
/dev/sda3           81920      163839       40960   83  Linux
</pre>
<p>首先看 Sector size 的大小，有logical/physical两个值，都是512字节。</p>
<p>sda1分区的Start=63，那么就表示sda1分区的起始扇区头距离LBA0的距离是63*512B=31.5KB，显然是未对齐的。</p>
<p>sda2分区的Start=40960，表示sda2分区的起始扇区头距离LBA0的距离是40960*512B=20MB=5*2*2MB，显然既在2MB大小的块边缘对齐，也在4MB大小的块边缘对齐；再看sda2分区的End=79871，表示sda2分区的终止扇区尾距离LBA0的距离是(79871+1)*512B=39MB，显然只能在1MB大小的块边缘对齐，但是不能在2MB/4MB块的边缘对齐。</p>
<p>sda3分区的Start=81920，表示sda3分区的起始扇区头距离LBA0的距离是81920*512B=40MB=5*4*2MB，显然既在2MB大小的块边缘对齐，也在4MB大小的块边缘对齐，甚至对于8MB大小的块也是对齐的；再看sda3分区的End=163839，表示sda3分区的终止扇区尾距离LBA0的距离是(163839+1)*512B=80MB=5*2*2*2*2MB，显然可以在2MB/4MB/8MB/16MB块的边缘对齐。</p>
<p>可见sda3是对齐最完美的分区，而sda1则是最糟糕的。</p>

<h2>文件系统</h2>
<p>现在SSD已经被完美的分区了，接下来就是创建文件系统了，那么哪个文件系统才是最适合SSD的呢？因为Linux系统有如此多的文件系统：etx2/ext3/ext4/reiser3/reiser4/JFS/XFS/Btrfs/NILFS2……在HDD的时代，选择合适的文件系统就一直是个令人头痛的问题。直到目前为止，不得不说，没有任何一个文件系统和SSD是绝配。</p>
<h3>未完待续。。。</h3>




<hr />

</body></html>